package com.fund.user.service.impl;

import com.fund.api.client.OrderClient;
import com.fund.api.client.ProductClient;
import com.fund.common.constant.StatusCodeConstant;
import com.fund.common.constant.SystemConstant;
import com.fund.common.enums.ClearingStatusEnum;
import com.fund.common.enums.SystemStatus;
import com.fund.common.enums.WorkdayType;
import com.fund.common.pojo.Result;
import com.fund.common.utils.HolidayChecker;
import com.fund.user.pojo.entity.Workday;
import com.fund.user.pojo.vo.ClearingStatus;
import com.fund.user.service.SystemService;
import com.fund.user.service.WorkdayService;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

@Service
public class SystemServiceImpl implements SystemService {

    @Autowired
    WorkdayService workdayService;

    @Autowired
    ProductClient productClient;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    OrderClient orderClient;
    @Override
    public Result<ClearingStatus> getStatus() {
        //获取状态
        String status = stringRedisTemplate.opsForValue().get(SystemConstant.SYSTEM_BUSINESS_STATUS_KEY);
        if(status == null){
            return Result.fail("系统状态不可用，等待查修");
        }
        //获取工作日
        String workDay = stringRedisTemplate.opsForValue().get(SystemConstant.WORK_DAY_KEY);
        if(workDay == null){
            return Result.fail("系统工作日不可用，等待查修");
        }
        //构建返回
        ClearingStatus clearingStatus = new ClearingStatus();
        clearingStatus.setWorkDay(workDay);
        clearingStatus.setIsOperating(status.equals(SystemStatus.BUSINESS.toString()));
        return Result.success(clearingStatus);
    }

    /**
     * 日初始化
     * @return
     */
    @Override
    public Result<Boolean> init() {
        //判断是否为工作日、节假日
        StringBuilder remark = new StringBuilder();
        LocalDateTime now = LocalDateTime.now();
        String dateString = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        //获取今日的类型（工作日、假期、周末）
        WorkdayType workdayType = HolidayChecker
                .getWorkdayType(String.valueOf(now.getYear()), dateString, remark);
        Workday workday = new Workday();
        workday.setRemark(remark.toString());
        workday.setDate(new Date());
        //用于存储工作日序号
        int workNumber = 0;
        //若是假期或者周末，那么工作日序号不变
        if(workdayType == WorkdayType.HOLIDAY ){
            workday.setIsHoliday(true);
            workday.setIsWorkday(false);
        }
        else if(workdayType == WorkdayType.WEEKEND){
            workday.setIsWorkday(false);
        }
        //若是工作日，那么当前工作日序号+1
        else {
            workday.setIsHoliday(false);
            workday.setIsWorkday(true);
            workNumber = Math.toIntExact(stringRedisTemplate.opsForValue().increment(SystemConstant.WORK_DAY_KEY));
        }
        //若序号仍为0，即非工作日，那么序号不变
        if(workNumber == 0) {
            workNumber = Integer.parseInt(stringRedisTemplate.opsForValue().get(SystemConstant.WORK_DAY_KEY));
        }
        workday.setWorkNumber(workNumber);

        //注：此处开始修改数据，注意事务
        //将清算状态变为运营中
        //TODO 此处有并发问题，后改为redisson或lua脚本
        String status = stringRedisTemplate.opsForValue().get(SystemConstant.SYSTEM_CLEARING_STATUS_KEY);
        if(status == null){
            return Result.fail("系统清算状态不可用");
        }
        //TODO 应有定时任务：下一天时把清算状态从已结算改为未初始化
        if(!status.equals(ClearingStatusEnum.WAITING_TO_INIT.toString())){
            return Result.fail("今日已初始化");
        }
        //将工作日信息存入数据库
        //TODO 非关键信息，可以使用MQ代替
        workdayService.save(workday);
        //开启柜台（即修改柜台状态）
        stringRedisTemplate.opsForValue()
                .set(SystemConstant.SYSTEM_BUSINESS_STATUS_KEY,SystemStatus.BUSINESS.toString());
        return Result.success(true);
    }

    /**
     * 日结算
     * @return
     */
    @GlobalTransactional
    @Override
    public Result<Boolean> settle() {
        //TODO 先备份数据库？
        //TODO 因为数据多，流程较长，使用websocket及时给予前端反馈
        //根据上一日净值数据，为所有产品生成今日净值，插入净值表
        Result<Boolean> updateWorthResult = productClient.updateWorth();
        if(!updateWorthResult.getCode().equals(StatusCodeConstant.SUCCESS_CODE)){
            return Result.fail(updateWorthResult.getMsg());
        }
        //注：先处理赎回订单，因为赎回后有钱拿，增加申购的成功率
        //处理所有当日赎回订单（工作日序号等于当前工作日），状态改为已完成，更新份额持有表，转钱
        //更新流水表和份额变化表
        orderClient.dealAllRedemptionOrders();
        //注：必须先处理待确认订单再处理当日申购订单，要么就使用工作日而非状态作为逻辑

        //先处理所有申购已有一日（即处于待确认状态的）订单，状态改为已完成，使份额到账，需要同时更新份额持有表和可用份额表
        //更新份额变化表

        //注：份额变化在修改份额持有表时顺便修改，不在当前服务中，仅是备注以防忘记

        //处理所有当日申购订单（即申购工作日序号等于当前工作日的），状态改为待确认，扣除余额，将订单状态改为待确认。需要校验余额是否充足

        //更新流水表
        //注：流水表也是顺便修改，不在当前服务中

        //更新清算状态为已结算
        return null;
    }
}
